#!/usr/bin/env python
# -*- coding: utf-8 -*-

__all__ = ['Compiler']
__version__ = '$Id$'

import re

import PyV8

from simplejson.encoder import JSONEncoder

class Compiler(object):
    """Compile and execute JavaScript code"""

    def __init__(self):
        self._encoder = JSONEncoder()

    def _insert_script(self, list, string):
        if string[0] == '=':
            list.append('__code__ += ' + str(string[1:]) + ';')
        else:
            list.append(str(string) + ';')

    def _insert_text(self, list, string):
        list.append('__code__ += ' + self._encoder.encode(string) + ';')

    def compile_string(self, string):
        """Convert script blocks in the given string to JavaScript blocks and
        wrap within a function call"""
        code = []
        code.append('(function(__code__) {')

        # When a line contains no text, remove whitespace
        string = re.sub(r'(\n+)[ \t]+(\<\?(?!=).*?\?\>)[ \t]*\n', '\\1\\2\n', string)
        # When a script block is at the end of on a line, remove new-line
        # character
        string = re.sub(r'\?\>\n', '?>', string)

        script_level = 0
        last_text_block, last_script_block = '', ''
        for i in range(0, len(string)):
            if '<?' == string[i:i + 2]:
                script_level += 1
                if script_level == 1 and len(last_text_block):
                    self._insert_text(code, last_text_block)
                last_text_block = ''

            if script_level:
                last_script_block += string[i]
            else:
                last_text_block += string[i]

            if '?>' == string[i - 1:i + 1] and script_level:
                script_level -= 1
                if script_level == 0 and len(last_script_block) > 4:
                    last_script_block = last_script_block[2:-2]
                    self._insert_script(code, last_script_block)
                    last_script_block = ''

        if script_level and len(last_script_block) > 2:
            self._insert_script(code, last_script_block)
        elif len(last_text_block):
            self._insert_text(code, last_text_block)

        code.append('return __code__;')
        code.append('})("")')

        return '\n'.join(code)

    def run_string(self, string, context=None):
        """Run the given code in the JavaScript VM and return the result"""
        engine = PyV8.JSEngine(context)
        result = engine.compile(string).run()
        if result is not None:
            result = unicode(result)
        return result

    def compile_and_run_string(self, string, context=None):
        """Convert script blocks in the given string to JavaScript blocks and
        wrap within a function call. Run the code in the JavaScript VM and
        return the result"""
        return self.run_string(self.compile_string(string), context)

    def __str__(self):
        return '<jsCSS.compiler.%s>' % (self.__class__.__name__, )
